/**************************************************************************
 *
 * Copyright 2014 MaeckySoft Ltd., Bludenz, Austria.
 * All Rights Reserved.
 *
 **************************************************************************/

#ifdef __unix__

#include <MOpenGL.h>
#include <MWindow.h>

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

//TODO: use std::string instead of (char *) -- Interface Only
//TODO: use cout/cerr instead of printf/fprintf -- Interface Only

MWindow::MWindow(int iDebug=0) : M3DBase(iDebug)
{
	if (iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> " << __PRETTY_FUNCTION__ << " called." << std::endl;

    m_MOGLOgl = new MOpenGL();
    //ctor
}

MWindow::~MWindow()
{
	if (m_iDebug >= MDBG_VERBOSE) std::cout << "(" << __FILE__ << ") <DBG_VERBOSE> " << __PRETTY_FUNCTION__ << " called." << std::endl;
    delete(m_MOGLOgl);
    //dtor
}

void MWindow::printMethodInfo(std::string method)
{
	if (m_iDebug == MDBG_ALL) std::cout << "(" << __FILE__ << ") <DBG_ALL> " << method << " called." << std::endl;
}


bool MWindow::createWindow(int iWinWidth, int iWinHeight, char *sDpyName)
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);
/*
  */
    //char *dpyName = NULL;
    //GLboolean printInfo = GL_FALSE;t
    EGLint egl_major, egl_minor;

    const char *s;

    DPYMain = XOpenDisplay(sDpyName);
    if (!DPYMain)
    {
        fprintf(stderr,"(%s) <Error> couldn't open display %s\n", __FILE__,
               sDpyName ? sDpyName : getenv("DISPLAY"));
        return -1;
    }

    EGLDMain = eglGetDisplay(DPYMain);
    if (!EGLDMain)
    {
        fprintf(stderr,"(%s) <Error> eglGetDisplay() failed\n",__FILE__);
        return -1;
    }

    if (!eglInitialize(EGLDMain, &egl_major, &egl_minor))
    {
        fprintf(stderr, "(%s) <Error> eglInitialize() failed\n",__FILE__);
        return -1;
    }


    if (m_iDebug >= MDBG_INFO)
    {
    s = eglQueryString(EGLDMain, EGL_VERSION);
    fprintf(stdout, "(%s) <DBG_INFO> EGL_Version = %s\n", __FILE__, s);

    s = eglQueryString(EGLDMain, EGL_VENDOR);
    fprintf(stdout, "(%s) <DBG_INFO> EGL_VENDOR = %s\n", __FILE__, s);

    s = eglQueryString(EGLDMain, EGL_EXTENSIONS);
    fprintf(stdout, "(%s) <DBG_INFO> EGL_EXTENSIONS = %s\n", __FILE__, s);

    s = eglQueryString(EGLDMain, EGL_CLIENT_APIS);
    fprintf(stdout, "(%s) <DBG_INFO> EGL_CLIENT_APIS = %s\n", __FILE__, s);


    }

    make_x_window(DPYMain, EGLDMain,
                  "Window", 0, 0, iWinWidth, iWinHeight,
                  &WINMain, &EGLCMain, &EGLSMain);

    XMapWindow(DPYMain, WINMain);

    if (!eglMakeCurrent(EGLDMain, EGLSMain, EGLSMain, EGLCMain))
    {
        fprintf(stderr,"(%s) <Error> eglMakeCurrent() failed\n",__FILE__);
        return -1;
    }


    m_MOGLOgl->init();
    m_MOGLOgl->reshape(iWinWidth, iWinHeight);


    event_loop(DPYMain, WINMain, EGLDMain, EGLSMain);


    eglDestroyContext(EGLDMain, EGLCMain);
    eglDestroySurface(EGLDMain, EGLSMain);
    eglTerminate(EGLDMain);


    XDestroyWindow(DPYMain, WINMain);
    XCloseDisplay(DPYMain);
    return true;
}
/*
 * Create an RGB, double-buffered X window.
 * Return the window and context handles.
 */
void MWindow::make_x_window(Display *DPLYMain,
                            EGLDisplay EGLDMain,
                            const char *sName,
                            int x, int y,
                            int iWidth, int iHeight,
                            Window *WINRet,
                            EGLContext *EGLCRet,
                            EGLSurface *EGLSRet)
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    static const EGLint EGLiAttribs[] =
    {
        EGL_RED_SIZE, 1,
        EGL_GREEN_SIZE, 1,
        EGL_BLUE_SIZE, 1,
        EGL_DEPTH_SIZE, 1,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_NONE
    };
#if USE_FULL_GL
    static const EGLint EGLiCtx_attribs[] =
    {
        EGL_NONE
    };
#else
    static const EGLint EGLiCtx_attribs[] =
    {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };
#endif

    int iScrnum;
    XSetWindowAttributes XSWAAttr;
    unsigned long lMask;
    Window WINRoot;
    Window WINMain;
    XVisualInfo *XVIVisInfo, XVIVisTemplate;
    int iNum_visuals;
    EGLContext EGLCMain;
    EGLConfig EGLCFMain;
    EGLint EGLiNum_configs;
    EGLint EGLiVid;

    iScrnum = DefaultScreen( DPLYMain);
    WINRoot = RootWindow( DPLYMain, iScrnum );

    if (!eglChooseConfig( EGLDMain, EGLiAttribs, &EGLCFMain, 1, &EGLiNum_configs))
    {
        fprintf(stderr,"(%s) <Error> couldn't get an EGL visual config\n",__FILE__);
        exit(1);
    }

    assert(EGLCFMain);
    assert(EGLiNum_configs > 0);

    if (!eglGetConfigAttrib(EGLDMain, EGLCFMain, EGL_NATIVE_VISUAL_ID, &EGLiVid))
    {
        fprintf(stderr,"(%s) <Error> eglGetConfigAttrib() failed\n",__FILE__);
        exit(1);
    }

    /* The X window visual must match the EGL config */
    XVIVisTemplate.visualid = EGLiVid;

    XVIVisInfo = XGetVisualInfo(DPYMain, VisualIDMask, &XVIVisTemplate, &iNum_visuals);
    if (!XVIVisInfo)
    {
        fprintf(stderr,"(%s) <Error> couldn't get X visual\n",__FILE__);
        exit(1);
    }

    /* window attributes */
    XSWAAttr.background_pixel = 0;
    XSWAAttr.border_pixel = 0;
    XSWAAttr.colormap = XCreateColormap( DPYMain, WINRoot, XVIVisInfo->visual, AllocNone);
    XSWAAttr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
    lMask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

    WINMain = XCreateWindow( DPYMain, WINRoot, 0, 0, iWidth, iHeight,
                         0, XVIVisInfo->depth, InputOutput,
                         XVIVisInfo->visual, lMask, &XSWAAttr );

    /* set hints and properties */
    {
        XSizeHints sizehints;
        sizehints.x = x;
        sizehints.y = y;
        sizehints.width  = iWidth;
        sizehints.height = iHeight;
        sizehints.flags = USSize | USPosition;
        XSetNormalHints(DPYMain, WINMain, &sizehints);
        XSetStandardProperties(DPYMain, WINMain, sName, sName,
                               None, (char **)NULL, 0, &sizehints);
    }

#if USE_FULL_GL /* XXX fix this when eglBindAPI() works */
    eglBindAPI(EGL_OPENGL_API);
#else
    eglBindAPI(EGL_OPENGL_ES_API);
#endif

    EGLCMain = eglCreateContext(EGLDMain, EGLCFMain, EGL_NO_CONTEXT, EGLiCtx_attribs );
    if (!EGLCMain)
    {
        fprintf(stderr,"(%s) <Error> eglCreateContext failed\n",__FILE__);
        exit(1);
    }

#if !USE_FULL_GL
    /* test eglQueryContext() */
    {
        EGLint EGLiVal;
        eglQueryContext(EGLDMain, EGLCMain, EGL_CONTEXT_CLIENT_VERSION, &EGLiVal);
        assert(EGLiVal == 2);
    }
#endif
    *EGLSRet = eglCreateWindowSurface(EGLDMain, EGLCFMain, WINMain, NULL);
    if (!*EGLSRet)
    {
        fprintf(stderr,"(%s) <Error> eglCreateWindowSurface failed\n",__FILE__);
        exit(1);
    }

    /* sanity checks */
    {
        EGLint EGLiVal;
        eglQuerySurface(EGLDMain, *EGLSRet, EGL_WIDTH, &EGLiVal);
        assert(EGLiVal == iWidth);
        eglQuerySurface(EGLDMain, *EGLSRet, EGL_HEIGHT, &EGLiVal);
        assert(EGLiVal == iHeight);
        assert(eglGetConfigAttrib(EGLDMain, EGLCFMain, EGL_SURFACE_TYPE, &EGLiVal));
        assert(EGLiVal & EGL_WINDOW_BIT);
    }

    XFree(XVIVisInfo);

    *WINRet = WINMain;
    *EGLSRet = EGLSMain;
    *EGLCRet = EGLCMain;
}

void MWindow::event_loop(Display *DPYMain,
                         Window WINMain,
                         EGLDisplay EGLDMain,
                         EGLSurface EGLSMain)
{
	if (m_iDebug >= MDBG_ALL) this->printMethodInfo(__PRETTY_FUNCTION__);

    while (1)
    {
        int iRedraw = 0;
        XEvent XEEvent;
        XNextEvent(DPYMain, &XEEvent);

        switch (XEEvent.type)
        {
        case Expose:
            iRedraw = 1;
            break;
        case ConfigureNotify:
            m_MOGLOgl->reshape(XEEvent.xconfigure.width, XEEvent.xconfigure.height);
            break;
        case KeyPress:
        {
            char cBuffer[10];
            int iCode, r;
            iCode = XLookupKeysym(&XEEvent.xkey, 0);
            if (m_iDebug >= MDBG_INFO) fprintf(stdout, "(%s) <DBG_INFO> Key Pressed: %d\n", __FILE__, iCode);

            if (iCode == XK_Left)
            {
                m_MOGLOgl->incViewRotY(0.01);
            }
            else if (iCode == XK_Right)
            {
                m_MOGLOgl->incViewRotY(-0.01);
            }
            else if (iCode == XK_Up)
            {
                m_MOGLOgl->incViewRotX(5.0);
            }
            else if (iCode == XK_Down)
            {
                m_MOGLOgl->incViewRotX(-5.0);
            }
            else
            {
                r = XLookupString(&XEEvent.xkey, cBuffer, sizeof(cBuffer),
                                  NULL, NULL);
                if (cBuffer[0] == 27)
                {
                    /* escape */
                    return;
                }

                if (r)
                {

                    //Error Handling
                }

            }
        }

        iRedraw = 1;
        break;
        default:
            ; /*no-op*/
        }

        if (iRedraw)
        {
            m_MOGLOgl->draw();
            eglSwapBuffers(EGLDMain, EGLSMain);
        }
    }
}


#endif
